<commit_before>/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// Removes duplicate functions. That can happen due to C++ templates,
// and also due to types being different at the source level, but
// identical when finally lowered into concrete wasm code.
//

#include <wasm.h>
#include <pass.h>
#include <ast_utils.h>

namespace wasm {

struct FunctionHasher : public WalkerPass<PostWalker<FunctionHasher, Visitor<FunctionHasher>>> {
  bool isFunctionParallel() override { return true; }

  FunctionHasher(std::map<Function*, uint32_t>* output) : output(output) {}

  FunctionHasher* create() override {
    return new FunctionHasher(output);
  }

  void doWalkFunction(Function* func) {
    assert(digest == 0);
    hash(func->getNumParams());
    for (auto type : func->params) hash(type);
    hash(func->getNumVars());
    for (auto type : func->vars) hash(type);
    hash(func->result);
    hash64(func->type.is() ? uint64_t(func->type.str) : uint64_t(0));
    hash(ExpressionAnalyzer::hash(func->body));
    output->at(func) = digest;
  }

private:
  std::map<Function*, uint32_t>* output;
  uint32_t digest = 0;

  void hash(uint32_t hash) {
    digest = rehash(digest, hash);
  }
  void hash64(uint64_t hash) {
    digest = rehash(rehash(digest, hash >> 32), uint32_t(hash));
  };
};

struct FunctionReplacer : public WalkerPass<PostWalker<FunctionReplacer, Visitor<FunctionReplacer>>> {
  bool isFunctionParallel() override { return true; }

  FunctionReplacer(std::map<Name, Name>* replacements) : replacements(replacements) {}

  FunctionReplacer* create() override {
    return new FunctionReplacer(replacements);
  }

  void visitCall(Call* curr) {
    auto iter = replacements->find(curr->target);
    if (iter != replacements->end()) {
      curr->target = iter->second;
    }
  }

private:
  std::map<Name, Name>* replacements;
};

struct DuplicateFunctionElimination : public Pass {
  void run(PassRunner* runner, Module* module) override {
    while (1) {
      // Hash all the functions
      hashes.clear();
      for (auto& func : module->functions) {
        hashes[func.get()] = 0; // ensure an entry for each function - we must not modify the map shape in parallel, just the values
      }
      PassRunner hasherRunner(module);
      hasherRunner.add<FunctionHasher>(&hashes);
      hasherRunner.run();
      // Find hash-equal groups
      std::map<uint32_t, std::vector<Function*>> hashGroups;
      for (auto& func : module->functions) {
        hashGroups[hashes[func.get()]].push_back(func.get());
      }
      // Find actually equal functions and prepare to replace them
      std::map<Name, Name> replacements;
      std::set<Name> duplicates;
      for (auto& pair : hashGroups) {
        auto& group = pair.second;
        if (group.size() == 1) continue;
        // pick a base for each group, and try to replace everyone else to it. TODO: multiple bases per hash group, for collisions
        Function* base = group[0];
        for (auto* func : group) {
          if (func != base && equal(func, base)) {
            replacements[func->name] = base->name;
            duplicates.insert(func->name);
          }
        }
      }
      // perform replacements
      if (replacements.size() > 0) {
        // remove the duplicates
        auto& v = module->functions;
        v.erase(std::remove_if(v.begin(), v.end(), [&](const std::unique_ptr<Function>& curr) {
          return duplicates.count(curr->name) > 0;
        }), v.end());
        module->updateFunctionsMap();
        // replace direct calls
        PassRunner replacerRunner(module);
        replacerRunner.add<FunctionReplacer>(&replacements);
        replacerRunner.run();
        // replace in table
        for (auto& segment : module->table.segments) {
          for (auto& name : segment.data) {
            auto iter = replacements.find(name);
            if (iter != replacements.end()) {
              name = iter->second;
            }
          }
        }
        // replace in start
        if (module->start.is()) {
          auto iter = replacements.find(module->start);
          if (iter != replacements.end()) {
            module->start = iter->second;
          }
        }
        // replace in exports
        for (auto& exp : module->exports) {
          auto iter = replacements.find(exp->value);
          if (iter != replacements.end()) {
            exp->value = iter->second;
          }
        }
      } else {
        break;
      }
    }
  }

private:
  std::map<Function*, uint32_t> hashes;

  bool equal(Function* left, Function* right) {
    if (left->getNumParams() != right->getNumParams()) return false;
    if (left->getNumVars() != right->getNumVars()) return false;
    for (Index i = 0; i < left->getNumLocals(); i++) {
      if (left->getLocalType(i) != right->getLocalType(i)) return false;
    }
    if (left->result != right->result) return false;
    if (left->type != right->type) return false;
    return ExpressionAnalyzer::equal(left->body, right->body);
  }
};

Pass *createDuplicateFunctionEliminationPass() {
  return new DuplicateFunctionElimination();
}

} // namespace wasm

<commit_msg>add option to remove duplicate funcs by name<commit_after>/*
 * Copyright 2016 WebAssembly Community Group participants
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//
// Removes duplicate functions. That can happen due to C++ templates,
// and also due to types being different at the source level, but
// identical when finally lowered into concrete wasm code.
//

#include <wasm.h>
#include <pass.h>
#include <ast_utils.h>

namespace wasm {

struct FunctionHasher : public WalkerPass<PostWalker<FunctionHasher, Visitor<FunctionHasher>>> {
  bool isFunctionParallel() override { return true; }

  FunctionHasher(std::map<Function*, uint32_t>* output) : output(output) {}

  FunctionHasher* create() override {
    return new FunctionHasher(output);
  }

  void doWalkFunction(Function* func) {
    assert(digest == 0);
    hash(func->getNumParams());
    for (auto type : func->params) hash(type);
    hash(func->getNumVars());
    for (auto type : func->vars) hash(type);
    hash(func->result);
    hash64(func->type.is() ? uint64_t(func->type.str) : uint64_t(0));
    hash(ExpressionAnalyzer::hash(func->body));
    output->at(func) = digest;
  }

private:
  std::map<Function*, uint32_t>* output;
  uint32_t digest = 0;

  void hash(uint32_t hash) {
    digest = rehash(digest, hash);
  }
  void hash64(uint64_t hash) {
    digest = rehash(rehash(digest, hash >> 32), uint32_t(hash));
  };
};

struct FunctionReplacer : public WalkerPass<PostWalker<FunctionReplacer, Visitor<FunctionReplacer>>> {
  bool isFunctionParallel() override { return true; }

  FunctionReplacer(std::map<Name, Name>* replacements) : replacements(replacements) {}

  FunctionReplacer* create() override {
    return new FunctionReplacer(replacements);
  }

  void visitCall(Call* curr) {
    auto iter = replacements->find(curr->target);
    if (iter != replacements->end()) {
      curr->target = iter->second;
    }
  }

private:
  std::map<Name, Name>* replacements;
};

struct DuplicateFunctionElimination : public Pass {
  void run(PassRunner* runner, Module* module) override {
    while (1) {
      // Hash all the functions
      hashes.clear();
      for (auto& func : module->functions) {
        hashes[func.get()] = 0; // ensure an entry for each function - we must not modify the map shape in parallel, just the values
      }
      PassRunner hasherRunner(module);
      hasherRunner.add<FunctionHasher>(&hashes);
      hasherRunner.run();
      // Find hash-equal groups
      std::map<uint32_t, std::vector<Function*>> hashGroups;
      for (auto& func : module->functions) {
        hashGroups[hashes[func.get()]].push_back(func.get());
      }
      // Find actually equal functions and prepare to replace them
      std::map<Name, Name> replacements;
      std::set<Name> duplicates;
      for (auto& pair : hashGroups) {
        auto& group = pair.second;
        if (group.size() == 1) continue;
        // pick a base for each group, and try to replace everyone else to it. TODO: multiple bases per hash group, for collisions
#if 0
        // for comparison purposes, pick in a deterministic way based on the names
        Function* base = nullptr;
        for (auto* func : group) {
          if (!base || strcmp(func->name.str, base->name.str) < 0) {
            base = func;
          }
        }
#else
        Function* base = group[0];
#endif
        for (auto* func : group) {
          if (func != base && equal(func, base)) {
            replacements[func->name] = base->name;
            duplicates.insert(func->name);
          }
        }
      }
      // perform replacements
      if (replacements.size() > 0) {
        // remove the duplicates
        auto& v = module->functions;
        v.erase(std::remove_if(v.begin(), v.end(), [&](const std::unique_ptr<Function>& curr) {
          return duplicates.count(curr->name) > 0;
        }), v.end());
        module->updateFunctionsMap();
        // replace direct calls
        PassRunner replacerRunner(module);
        replacerRunner.add<FunctionReplacer>(&replacements);
        replacerRunner.run();
        // replace in table
        for (auto& segment : module->table.segments) {
          for (auto& name : segment.data) {
            auto iter = replacements.find(name);
            if (iter != replacements.end()) {
              name = iter->second;
            }
          }
        }
        // replace in start
        if (module->start.is()) {
          auto iter = replacements.find(module->start);
          if (iter != replacements.end()) {
            module->start = iter->second;
          }
        }
        // replace in exports
        for (auto& exp : module->exports) {
          auto iter = replacements.find(exp->value);
          if (iter != replacements.end()) {
            exp->value = iter->second;
          }
        }
      } else {
        break;
      }
    }
  }

private:
  std::map<Function*, uint32_t> hashes;

  bool equal(Function* left, Function* right) {
    if (left->getNumParams() != right->getNumParams()) return false;
    if (left->getNumVars() != right->getNumVars()) return false;
    for (Index i = 0; i < left->getNumLocals(); i++) {
      if (left->getLocalType(i) != right->getLocalType(i)) return false;
    }
    if (left->result != right->result) return false;
    if (left->type != right->type) return false;
    return ExpressionAnalyzer::equal(left->body, right->body);
  }
};

Pass *createDuplicateFunctionEliminationPass() {
  return new DuplicateFunctionElimination();
}

} // namespace wasm

<|endoftext|>


<commit_before>#include <climits>
#include <cmath>
#include <ctime>
#include <fstream>
#include <iostream>
#include <sstream>
#include <unistd.h>

#include "../include/solvers/DeterministicSolver.h"
#include "../include/solvers/HDPSolver.h"
#include "../include/solvers/HMinHeuristic.h"
#include "../include/solvers/LAOStarSolver.h"
#include "../include/solvers/LRTDPSolver.h"
#include "../include/solvers/FLARESSolver.h"
#include "../include/solvers/Solver.h"
#include "../include/solvers/SSiPPSolver.h"
#include "../include/solvers/UCTSolver.h"
#include "../include/solvers/VISolver.h"

#include "../include/util/flags.h"
#include "../include/util/general.h"
#include "../include/util/graph.h"

#include "../include/domains/ctp/CTPOptimisticHeuristic.h"
#include "../include/domains/ctp/CTPProblem.h"
#include "../include/domains/ctp/CTPState.h"

#include "../include/domains/gridworld/GridWorldProblem.h"
#include "../include/domains/gridworld/GWManhattanHeuristic.h"

#include "../include/domains/racetrack/RacetrackProblem.h"
#include "../include/domains/racetrack/RTrackDetHeuristic.h"

#include "../include/domains/sailing/SailingNoWindHeuristic.h"
#include "../include/domains/sailing/SailingProblem.h"

using namespace mdplib;
using namespace mlcore;
using namespace mlsolvers;
using namespace std;

Problem* problem = nullptr;
Heuristic* heuristic = nullptr;
Solver* solver = nullptr;
string algorithm = "greedy";

int verbosity = 0;
bool useOnline = false;

void setupRacetrack()
{
    string trackName = flag_value("track");
    if (verbosity > 100)
        cout << "Setting up racetrack " << trackName << endl;
    problem = new RacetrackProblem(trackName.c_str());
    ((RacetrackProblem*) problem)->pError(0.20);
    ((RacetrackProblem*) problem)->pSlip(0.10);
    ((RacetrackProblem*) problem)->mds(-1);
    if (!flag_is_registered_with_value("heuristic") ||
            flag_value("heuristic") == "domain")
        heuristic = new RTrackDetHeuristic(trackName.c_str());
}

void setupGridWorld()
{
    string grid = flag_value("grid");
    if (verbosity > 100)
        cout << "Setting up grid world " << grid << endl;
    problem = new GridWorldProblem(grid.c_str(), 1.0, 50.0, true);
    if (!flag_is_registered_with_value("heuristic") ||
            flag_value("heuristic") == "domain")
        heuristic = new GWManhattanHeuristic((GridWorldProblem*) problem);
}

void setupSailingDomain()
{
    static vector<double> costs;
    costs.push_back(1);
    costs.push_back(2);
    costs.push_back(5);
    costs.push_back(10);
    costs.push_back(mdplib::dead_end_cost + 1);

    static double windTransition[] = {
        0.20, 0.20, 0.20, 0.00, 0.00, 0.00, 0.20, 0.20,
        0.20, 0.20, 0.20, 0.20, 0.00, 0.00, 0.00, 0.20,
        0.20, 0.20, 0.20, 0.20, 0.20, 0.00, 0.00, 0.00,
        0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.00, 0.00,
        0.00, 0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.00,
        0.00, 0.00, 0.00, 0.20, 0.20, 0.20, 0.20, 0.20,
        0.20, 0.00, 0.00, 0.00, 0.20, 0.20, 0.20, 0.20,
        0.20, 0.20, 0.00, 0.00, 0.00, 0.20, 0.20, 0.20};

    if (!flag_is_registered_with_value("sailing-goal")) {
        cerr << "Must specify sailing-goal argument flag" << endl;
        exit(-1);
    }

    int sizeSailing = atoi(flag_value("sailing-size").c_str());
    int goalSailing = atoi(flag_value("sailing-goal").c_str());

    if (verbosity > 100)
        cout << "Setting up sailing domain with size " << sizeSailing <<
            " with goal " << goalSailing << endl;

    problem =
        new SailingProblem(0, 0, 0,
                           goalSailing, goalSailing,
                           sizeSailing, sizeSailing,
                           costs,
                           windTransition);

    if (!flag_is_registered_with_value("heuristic") ||
            flag_value("heuristic") == "domain")
        heuristic =
            new SailingNoWindHeuristic(static_cast<SailingProblem*>(problem));
}

void setupCTP()
{
    if (verbosity > 100) {
        cout << "Setting up Canadian Traveler Problem " <<
            flag_value("ctp") << endl;
    }
    problem = new CTPProblem(flag_value("ctp").c_str());
    if (!flag_is_registered_with_value("heuristic") ||
            flag_value("heuristic") == "domain")
        heuristic =
            new CTPOptimisticHeuristic(static_cast<CTPProblem*> (problem));
}

void setupProblem()
{
    if (verbosity > 100)
        cout << "Setting up problem" << endl;
    if (flag_is_registered_with_value("track")) {
        setupRacetrack();
    } else if (flag_is_registered_with_value("grid")) {
        setupGridWorld();
    } else if (flag_is_registered_with_value("sailing-size")) {
        setupSailingDomain();
    } else if (flag_is_registered_with_value("ctp")) {
        setupCTP();
    } else {
        cerr << "Invalid problem." << endl;
        exit(-1);
    }
}

bool mustReplan(State* s, int plausTrial) {
  if (flag_is_registered("online"))
      return true;
  if (algorithm == "flares") {
      return !s->checkBits(mdplib::SOLVED_FLARES);
  }
  if (algorithm == "hdp") {
      if (flag_is_registered("i")) {
          int j = INT_MAX;
          if (flag_is_registered_with_value("j")) {
              j = stoi(flag_value("j"));
          }
          if (plausTrial >= j) {
              static_cast<HDPSolver*>(solver)->clearLabels();
              return true;
          }
      }
  }
  if (algorithm == "ssipp") {
    return true;
  }
  return false;
}

void initSolver()
{
    double tol = 1.0e-3;
    assert(flag_is_registered_with_value("algorithm"));
    algorithm = flag_value("algorithm");

    int horizon = 0, expansions = 1, trials = 1000;
    if (flag_is_registered_with_value("horizon"))
        horizon = stoi(flag_value("horizon"));
    if (flag_is_registered_with_value("expansions"))
        expansions = stoi(flag_value("expansions"));
    if (flag_is_registered_with_value("trials"))
        trials = stoi(flag_value("trials"));

    if (algorithm == "wlao") {
        double weight = 1.0;
        if (flag_is_registered_with_value("weight"))
            weight = stof(flag_value("weight"));
        solver = new LAOStarSolver(problem, tol, 1000000, weight);
    } else if (algorithm == "lao") {
        solver = new LAOStarSolver(problem, tol, 1000000);
    } else if (algorithm == "lrtdp") {
        solver = new LRTDPSolver(problem, trials, tol);
    } else if (algorithm == "flares") {
        bool optimal = flag_is_registered("optimal");
        solver = new FLARESSolver(problem, trials, tol, horizon, optimal);
    } else if (algorithm == "hdp") {
        int plaus;
        if (flag_is_registered_with_value("i"))
            solver = new HDPSolver(problem, tol, stoi(flag_value("i")));
        else
            solver = new HDPSolver(problem, tol);
    } else if (algorithm == "vi") {
        solver = new VISolver(problem, 1000000000, tol);
    } else if (algorithm == "ssipp") {
        solver = new SSiPPSolver(problem, tol, horizon);
    } else if (algorithm == "labeled-ssipp") {
        solver = new SSiPPSolver(problem, tol, horizon, SSiPPAlgo::Labeled);
    } else if (algorithm == "det") {
        solver = new DeterministicSolver(problem,
                                         mlsolvers::det_most_likely,
                                         heuristic);
    } else if (algorithm != "greedy") {
        cerr << "Unknown algorithm: " << algorithm << endl;
        assert(false);
    }
}

void updateStatistics(double cost, int n, double& mean, double& M2)
{
    double delta = cost - mean;
    mean += delta / n;
    M2 += delta * (cost - mean);
}

int main(int argc, char* args[])
{
    register_flags(argc, args);

    verbosity = 0;
    if (flag_is_registered_with_value("v"))
        verbosity = stoi(flag_value("v"));
    if (flag_is_registered("debug"))
        mdplib_debug = true;
    setupProblem();
    if (!flag_is_registered("dont-generate"))
        problem->generateAll();
    if (flag_is_registered_with_value("heuristic") &&
            flag_value("heuristic") == "hmin") {
        clock_t startTime = clock();
        bool solveAll = flag_is_registered("hmin-solve-all");
        heuristic = new HMinHeuristic(problem, solveAll);
        clock_t endTime = clock();
        if (verbosity > 100) {
            cout << "Heuristic took " <<
                (double(endTime - startTime) / CLOCKS_PER_SEC) <<
                " seconds."  << endl;
        }
    }
    problem->setHeuristic(heuristic);

    if (verbosity > 100)
        cout << problem->states().size() << " states" << endl;

    initSolver();

    int nsims = 100;
    if (flag_is_registered_with_value("n"))
        nsims = stoi(flag_value("n"));

    // Running simulations to evaluate the solver's performance.
    double expectedCost = 0.0;
    double variance = 0.0;
    double expectedTime = 0.0;
    StateSet statesSeen;

    int cnt = 0;
    int numDecisions = 0;
    for (int i = 0; i < nsims; i++) {
        if (verbosity >= 100)
            cout << " ********* Simulation Starts ********* " << endl;
        clock_t startTime, endTime;
        if (i == 0 || !flag_is_registered("no-initial-plan")) {
            for (State* s : problem->states())
                s->reset();
            startTime = clock();
            if (algorithm != "greedy")
                solver->solve(problem->initialState());
            endTime = clock();
            expectedTime += (double(endTime - startTime) / CLOCKS_PER_SEC);
            numDecisions++;
        }
        if (verbosity >= 10) {
            cout << "Starting simulation " << i << endl;
        }
        State* tmp = problem->initialState();
        if (verbosity >= 100) {
            cout << "Estimated cost " <<
                problem->initialState()->cost() << endl << tmp << " ";
        }
        double costTrial = 0.0;
        int plausTrial = 0;
        while (!problem->goal(tmp)) {
            statesSeen.insert(tmp);
            Action* a;
            if (mustReplan(tmp, plausTrial)) {
                startTime = clock();
                if (algorithm != "greedy")
                    a = solver->solve(tmp);
                else
                    a = greedyAction(problem, tmp);
                endTime = clock();
                expectedTime += (double(endTime - startTime) / CLOCKS_PER_SEC);
                numDecisions++;
            }
            a = greedyAction(problem, tmp);

            if (verbosity >= 1000) {
                cout << tmp << " " << a << " " << endl;
            }

            costTrial += problem->cost(tmp, a);
            if (costTrial >= mdplib::dead_end_cost) {
                break;
            }
            double prob = 0.0;
            State* aux = randomSuccessor(problem, tmp, a, &prob);
            if (flag_value("algorithm") == "hdp") {
                double maxProb = 0.0;
                for (auto const & sccr : problem->transition(tmp, a))
                    maxProb = std::max(maxProb, sccr.su_prob);
                plausTrial +=
                    static_cast<HDPSolver*>(solver)->kappa(prob, maxProb);
            }
            tmp = aux;

        }
        if (flag_is_registered("ctp")) {
            CTPState* ctps = static_cast<CTPState*>(tmp);
            if (!ctps->badWeather()) {
                cnt++;
                updateStatistics(costTrial, cnt, expectedCost, variance);
            }
        } else {
            cnt++;
            updateStatistics(costTrial, cnt, expectedCost, variance);
            if (verbosity >= 1)
                cout << costTrial << endl;
        }
        if (verbosity >= 100)
            cout << endl;
    }

    if (verbosity >= 1) {
        cout << "Estimated cost " << problem->initialState()->cost() << " ";
        cout << "Avg. Exec cost " << expectedCost << " ";
        cout << "Std. Dev. " << sqrt(variance / (cnt - 1)) << " ";
        cout << "Total time " << expectedTime / cnt << " " << endl;
        cout << "States seen " << statesSeen.size() << endl;
        cout << "Avg. time per decision " <<
            expectedTime / numDecisions << endl;
    } else {
        cout << problem->initialState()->cost() << " ";
        cout << expectedCost << " " << sqrt(variance / (cnt - 1)) << " " <<
            expectedTime / cnt << " " << expectedTime / numDecisions << endl;
    }

    delete problem;
    delete heuristic;
    delete solver;
}

<commit_msg>minor changes<commit_after>#include <climits>
#include <cmath>
#include <ctime>
#include <fstream>
#include <iostream>
#include <sstream>
#include <unistd.h>

#include "../include/solvers/DeterministicSolver.h"
#include "../include/solvers/HDPSolver.h"
#include "../include/solvers/HMinHeuristic.h"
#include "../include/solvers/LAOStarSolver.h"
#include "../include/solvers/LRTDPSolver.h"
#include "../include/solvers/FLARESSolver.h"
#include "../include/solvers/Solver.h"
#include "../include/solvers/SSiPPSolver.h"
#include "../include/solvers/UCTSolver.h"
#include "../include/solvers/VISolver.h"

#include "../include/util/flags.h"
#include "../include/util/general.h"
#include "../include/util/graph.h"

#include "../include/domains/ctp/CTPOptimisticHeuristic.h"
#include "../include/domains/ctp/CTPProblem.h"
#include "../include/domains/ctp/CTPState.h"

#include "../include/domains/gridworld/GridWorldProblem.h"
#include "../include/domains/gridworld/GWManhattanHeuristic.h"

#include "../include/domains/racetrack/RacetrackProblem.h"
#include "../include/domains/racetrack/RTrackDetHeuristic.h"

#include "../include/domains/sailing/SailingNoWindHeuristic.h"
#include "../include/domains/sailing/SailingProblem.h"

using namespace mdplib;
using namespace mlcore;
using namespace mlsolvers;
using namespace std;

Problem* problem = nullptr;
Heuristic* heuristic = nullptr;
Solver* solver = nullptr;
string algorithm = "greedy";

int verbosity = 0;
bool useOnline = false;

void setupRacetrack()
{
    string trackName = flag_value("track");
    if (verbosity > 100)
        cout << "Setting up racetrack " << trackName << endl;
    problem = new RacetrackProblem(trackName.c_str());
    ((RacetrackProblem*) problem)->pError(0.20);
    ((RacetrackProblem*) problem)->pSlip(0.10);
    ((RacetrackProblem*) problem)->mds(-1);
    if (!flag_is_registered_with_value("heuristic") ||
            flag_value("heuristic") == "domain")
        heuristic = new RTrackDetHeuristic(trackName.c_str());
}

void setupGridWorld()
{
    string grid = flag_value("grid");
    if (verbosity > 100)
        cout << "Setting up grid world " << grid << endl;
    problem = new GridWorldProblem(grid.c_str(), 1.0, 50.0, true);
    if (!flag_is_registered_with_value("heuristic") ||
            flag_value("heuristic") == "domain")
        heuristic = new GWManhattanHeuristic((GridWorldProblem*) problem);
}

void setupSailingDomain()
{
    static vector<double> costs;
    costs.push_back(1);
    costs.push_back(2);
    costs.push_back(5);
    costs.push_back(10);
    costs.push_back(mdplib::dead_end_cost + 1);

    static double windTransition[] = {
        0.20, 0.20, 0.20, 0.00, 0.00, 0.00, 0.20, 0.20,
        0.20, 0.20, 0.20, 0.20, 0.00, 0.00, 0.00, 0.20,
        0.20, 0.20, 0.20, 0.20, 0.20, 0.00, 0.00, 0.00,
        0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.00, 0.00,
        0.00, 0.00, 0.20, 0.20, 0.20, 0.20, 0.20, 0.00,
        0.00, 0.00, 0.00, 0.20, 0.20, 0.20, 0.20, 0.20,
        0.20, 0.00, 0.00, 0.00, 0.20, 0.20, 0.20, 0.20,
        0.20, 0.20, 0.00, 0.00, 0.00, 0.20, 0.20, 0.20};

    if (!flag_is_registered_with_value("sailing-goal")) {
        cerr << "Must specify sailing-goal argument flag" << endl;
        exit(-1);
    }

    int sizeSailing = atoi(flag_value("sailing-size").c_str());
    int goalSailing = atoi(flag_value("sailing-goal").c_str());

    if (verbosity > 100)
        cout << "Setting up sailing domain with size " << sizeSailing <<
            " with goal " << goalSailing << endl;

    problem =
        new SailingProblem(0, 0, 0,
                           goalSailing, goalSailing,
                           sizeSailing, sizeSailing,
                           costs,
                           windTransition);

    if (!flag_is_registered_with_value("heuristic") ||
            flag_value("heuristic") == "domain")
        heuristic =
            new SailingNoWindHeuristic(static_cast<SailingProblem*>(problem));
}

void setupCTP()
{
    if (verbosity > 100) {
        cout << "Setting up Canadian Traveler Problem " <<
            flag_value("ctp") << endl;
    }
    problem = new CTPProblem(flag_value("ctp").c_str());
    if (!flag_is_registered_with_value("heuristic") ||
            flag_value("heuristic") == "domain")
        heuristic =
            new CTPOptimisticHeuristic(static_cast<CTPProblem*> (problem));
}

void setupProblem()
{
    if (verbosity > 100)
        cout << "Setting up problem" << endl;
    if (flag_is_registered_with_value("track")) {
        setupRacetrack();
    } else if (flag_is_registered_with_value("grid")) {
        setupGridWorld();
    } else if (flag_is_registered_with_value("sailing-size")) {
        setupSailingDomain();
    } else if (flag_is_registered_with_value("ctp")) {
        setupCTP();
    } else {
        cerr << "Invalid problem." << endl;
        exit(-1);
    }
}

bool mustReplan(State* s, int plausTrial) {
  if (flag_is_registered("online"))
      return true;
  if (algorithm == "flares") {
      return !s->checkBits(mdplib::SOLVED_FLARES);
  }
  if (algorithm == "hdp") {
      if (flag_is_registered("i")) {
          int j = INT_MAX;
          if (flag_is_registered_with_value("j")) {
              j = stoi(flag_value("j"));
          }
          if (plausTrial >= j) {
              static_cast<HDPSolver*>(solver)->clearLabels();
              return true;
          }
      }
  }
  if (algorithm == "ssipp") {
    return true;
  }
  return false;
}

void initSolver()
{
    double tol = 1.0e-3;
    assert(flag_is_registered_with_value("algorithm"));
    algorithm = flag_value("algorithm");

    int horizon = 0, expansions = 1, trials = 1000;
    if (flag_is_registered_with_value("horizon"))
        horizon = stoi(flag_value("horizon"));
    if (flag_is_registered_with_value("expansions"))
        expansions = stoi(flag_value("expansions"));
    if (flag_is_registered_with_value("trials"))
        trials = stoi(flag_value("trials"));

    if (algorithm == "wlao") {
        double weight = 1.0;
        if (flag_is_registered_with_value("weight"))
            weight = stof(flag_value("weight"));
        solver = new LAOStarSolver(problem, tol, 1000000, weight);
    } else if (algorithm == "lao") {
        solver = new LAOStarSolver(problem, tol, 1000000);
    } else if (algorithm == "lrtdp") {
        solver = new LRTDPSolver(problem, trials, tol);
    } else if (algorithm == "flares") {
        bool optimal = flag_is_registered("optimal");
        solver = new FLARESSolver(problem, trials, tol, horizon, optimal);
    } else if (algorithm == "hdp") {
        int plaus;
        if (flag_is_registered_with_value("i"))
            solver = new HDPSolver(problem, tol, stoi(flag_value("i")));
        else
            solver = new HDPSolver(problem, tol);
    } else if (algorithm == "vi") {
        solver = new VISolver(problem, 1000000000, tol);
    } else if (algorithm == "ssipp") {
        solver = new SSiPPSolver(problem, tol, horizon);
    } else if (algorithm == "labeled-ssipp") {
        solver = new SSiPPSolver(problem, tol, horizon, SSiPPAlgo::Labeled);
    } else if (algorithm == "det") {
        solver = new DeterministicSolver(problem,
                                         mlsolvers::det_most_likely,
                                         heuristic);
    } else if (algorithm != "greedy") {
        cerr << "Unknown algorithm: " << algorithm << endl;
        assert(false);
    }
}

void updateStatistics(double cost, int n, double& mean, double& M2)
{
    double delta = cost - mean;
    mean += delta / n;
    M2 += delta * (cost - mean);
}

int main(int argc, char* args[])
{
    register_flags(argc, args);

    verbosity = 0;
    if (flag_is_registered_with_value("v"))
        verbosity = stoi(flag_value("v"));
    if (flag_is_registered("debug"))
        mdplib_debug = true;
    setupProblem();
    if (!flag_is_registered("dont-generate"))
        problem->generateAll();
    if (flag_is_registered_with_value("heuristic")) {
        if (flag_value("heuristic") == "hmin") {
            clock_t startTime = clock();
            bool solveAll = flag_is_registered("hmin-solve-all");
            heuristic = new HMinHeuristic(problem, solveAll);
            clock_t endTime = clock();
            if (verbosity > 100) {
                cout << "Heuristic took " <<
                    (double(endTime - startTime) / CLOCKS_PER_SEC) <<
                    " seconds."  << endl;
            }
        } else if (flag_value("heuristic") == "zero")
            heuristic = nullptr;
    }
    problem->setHeuristic(heuristic);

    if (verbosity > 100)
        cout << problem->states().size() << " states" << endl;

    initSolver();

    int nsims = 100;
    if (flag_is_registered_with_value("n"))
        nsims = stoi(flag_value("n"));

    // Running simulations to evaluate the solver's performance.
    double expectedCost = 0.0;
    double variance = 0.0;
    double expectedTime = 0.0;
    StateSet statesSeen;

    int cnt = 0;
    int numDecisions = 0;
    for (int i = 0; i < nsims; i++) {
        if (verbosity >= 100)
            cout << " ********* Simulation Starts ********* " << endl;
        clock_t startTime, endTime;
        if (i == 0 || !flag_is_registered("no-initial-plan")) {
            for (State* s : problem->states())
                s->reset();
            startTime = clock();
            if (algorithm != "greedy")
                solver->solve(problem->initialState());
            endTime = clock();
            expectedTime += (double(endTime - startTime) / CLOCKS_PER_SEC);
            numDecisions++;
        }
        if (verbosity >= 10) {
            cout << "Starting simulation " << i << endl;
        }
        State* tmp = problem->initialState();
        if (verbosity >= 100) {
            cout << "Estimated cost " <<
                problem->initialState()->cost() << endl << tmp << " ";
        }
        double costTrial = 0.0;
        int plausTrial = 0;
        while (!problem->goal(tmp)) {
            statesSeen.insert(tmp);
            Action* a;
            if (mustReplan(tmp, plausTrial)) {
                startTime = clock();
                if (algorithm != "greedy")
                    a = solver->solve(tmp);
                else
                    a = greedyAction(problem, tmp);
                endTime = clock();
                expectedTime += (double(endTime - startTime) / CLOCKS_PER_SEC);
                numDecisions++;
            }
            a = greedyAction(problem, tmp);

            if (verbosity >= 1000) {
                cout << tmp << " " << a << " " << endl;
            }

            costTrial += problem->cost(tmp, a);
            if (costTrial >= mdplib::dead_end_cost) {
                break;
            }
            double prob = 0.0;
            State* aux = randomSuccessor(problem, tmp, a, &prob);
            if (flag_value("algorithm") == "hdp") {
                double maxProb = 0.0;
                for (auto const & sccr : problem->transition(tmp, a))
                    maxProb = std::max(maxProb, sccr.su_prob);
                plausTrial +=
                    static_cast<HDPSolver*>(solver)->kappa(prob, maxProb);
            }
            tmp = aux;

        }
        if (flag_is_registered("ctp")) {
            CTPState* ctps = static_cast<CTPState*>(tmp);
            if (!ctps->badWeather()) {
                cnt++;
                updateStatistics(costTrial, cnt, expectedCost, variance);
            }
        } else {
            cnt++;
            updateStatistics(costTrial, cnt, expectedCost, variance);
            if (verbosity >= 1)
                cout << costTrial << endl;
        }
        if (verbosity >= 100)
            cout << endl;
    }

    if (verbosity >= 1) {
        cout << "Estimated cost " << problem->initialState()->cost() << " ";
        cout << "Avg. Exec cost " << expectedCost << " ";
        cout << "Std. Dev. " << sqrt(variance / (cnt - 1)) << " ";
        cout << "Total time " << expectedTime / cnt << " " << endl;
        cout << "States seen " << statesSeen.size() << endl;
        cout << "Avg. time per decision " <<
            expectedTime / numDecisions << endl;
    } else {
        cout << problem->initialState()->cost() << " ";
        cout << expectedCost << " " << sqrt(variance / (cnt - 1)) << " " <<
            expectedTime / cnt << " " << expectedTime / numDecisions << endl;
    }

    delete problem;
    delete heuristic;
    delete solver;
}

<|endoftext|>


<commit_before>/*
 *  ParsedExpressionVariableTest.cpp
 *  MWorksCore
 *
 *  Created by David Cox on 1/1/08.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */

#include "ParsedExpressionVariableTest.h"

#include "VariableRegistry.h"
#include "TrialBuildingBlocks.h"
#include "EventBuffer.h"
using namespace mw;

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ParsedExpressionVariableTestFixture, "Unit Test" );

void ParsedExpressionVariableTestFixture::testSimpleExpression() {

	shared_ptr<VariableRegistry> r(new VariableRegistry(global_outgoing_event_buffer));
	global_variable_registry = r;

 Datum testInt((float)1);
	VariableProperties props(&testInt,
							 "test",
							 "Test test",
							 "Testy test test test",
							 M_NEVER,
							 M_WHEN_CHANGED,
							 true,
							 false,
							 M_CONTINUOUS_INFINITE,
							 "");

	shared_ptr<Variable> testvar =
	r->createGlobalVariable(&props);

	shared_ptr<ParsedExpressionVariable> e(new ParsedExpressionVariable("1 + 1"));
 Datum data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 2 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("20 % 2"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("test + 1"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 2 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("4-2"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 2 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("20/10"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 2 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("20*3"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 60 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(test + 1)%2"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("test%20"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 1 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("sqrt(test+8)"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 3 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(0 #OR 1)"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 1 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(0 #AND 1)"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(0 #GT 1)"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("!1"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("!0"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data != 0 );

	bool threw_exception = false;
	try {
		e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(0 #GT 1))"));
	} catch (stx::ExpressionParserException &e) {
		threw_exception = true;
	}

	CPPUNIT_ASSERT( threw_exception );

	threw_exception = false;
	try {
		e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("other_var + 1"));
	} catch (SimpleException &e) {
		threw_exception = true;
	}

	CPPUNIT_ASSERT( threw_exception );

}

<commit_msg>Fixed unit test broken by changes to expression parser exception handling<commit_after>/*
 *  ParsedExpressionVariableTest.cpp
 *  MWorksCore
 *
 *  Created by David Cox on 1/1/08.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */

#include "ParsedExpressionVariableTest.h"

#include "VariableRegistry.h"
#include "TrialBuildingBlocks.h"
#include "EventBuffer.h"
using namespace mw;

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ParsedExpressionVariableTestFixture, "Unit Test" );

void ParsedExpressionVariableTestFixture::testSimpleExpression() {

	shared_ptr<VariableRegistry> r(new VariableRegistry(global_outgoing_event_buffer));
	global_variable_registry = r;

 Datum testInt((float)1);
	VariableProperties props(&testInt,
							 "test",
							 "Test test",
							 "Testy test test test",
							 M_NEVER,
							 M_WHEN_CHANGED,
							 true,
							 false,
							 M_CONTINUOUS_INFINITE,
							 "");

	shared_ptr<Variable> testvar =
	r->createGlobalVariable(&props);

	shared_ptr<ParsedExpressionVariable> e(new ParsedExpressionVariable("1 + 1"));
 Datum data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 2 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("20 % 2"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("test + 1"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 2 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("4-2"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 2 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("20/10"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 2 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("20*3"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 60 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(test + 1)%2"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("test%20"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 1 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("sqrt(test+8)"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 3 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(0 #OR 1)"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 1 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(0 #AND 1)"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(0 #GT 1)"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("!1"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data == 0 );

	e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("!0"));
	data = e->getValue();

	CPPUNIT_ASSERT( (long)data != 0 );

	bool threw_exception = false;
	try {
		e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("(0 #GT 1))"));
	} catch (FatalParserException &e) {
		threw_exception = true;
	}

	CPPUNIT_ASSERT( threw_exception );

	threw_exception = false;
	try {
		e = shared_ptr<ParsedExpressionVariable>(new ParsedExpressionVariable("other_var + 1"));
	} catch (SimpleException &e) {
		threw_exception = true;
	}

	CPPUNIT_ASSERT( threw_exception );

}

<|endoftext|>


<commit_before>#include "qmloutlinemodel.h"
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/qmljsinterpreter.h>
#include <qmljs/qmljslookupcontext.h>

#include <coreplugin/icore.h>
#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <typeinfo>

using namespace QmlJS;
using namespace QmlJSEditor::Internal;

enum {
    debug = true
};

namespace QmlJSEditor {
namespace Internal {

class QmlOutlineModelSync : protected AST::Visitor
{
public:
    QmlOutlineModelSync(QmlOutlineModel *model) :
        m_model(model),
        indent(0)
    {
    }

    void operator()(Document::Ptr doc, const Snapshot &snapshot)
    {
        m_nodeToIndex.clear();

        // Set up lookup context once to do the element type lookup
        //
        // We're simplifying here by using the root context everywhere
        // (empty node list). However, creating the LookupContext is quite expensive (about 3ms),
        // and there is AFAIK no way to introduce new type names in a sub-context.
        m_context = LookupContext::create(doc, snapshot, QList<AST::Node*>());

        if (debug)
            qDebug() << "QmlOutlineModel ------";
        if (doc && doc->ast())
            doc->ast()->accept(this);

        m_context.clear();
    }

private:
    bool preVisit(AST::Node *node)
    {
        if (!node)
            return false;
        if (debug)
            qDebug() << "QmlOutlineModel -" << QByteArray(indent++, '-').constData() << node << typeid(*node).name();
        return true;
    }

    void postVisit(AST::Node *)
    {
        indent--;
    }

    QString asString(AST::UiQualifiedId *id)
    {
        QString text;
        for (; id; id = id->next) {
            if (id->name)
                text += id->name->asString();
            else
                text += QLatin1Char('?');

            if (id->next)
                text += QLatin1Char('.');
        }

        return text;
    }

    typedef QPair<QString,QString> ElementType;
    bool visit(AST::UiObjectDefinition *objDef)
    {
        if (!validElement(objDef)) {
            return true;
        }

        AST::SourceLocation location = getLocation(objDef);

        const QString typeName = asString(objDef->qualifiedTypeNameId);

        if (!m_typeToIcon.contains(typeName)) {
            m_typeToIcon.insert(typeName, getIcon(objDef));
        }
        const QIcon icon = m_typeToIcon.value(typeName);
        QString id = getId(objDef);

        QModelIndex index = m_model->enterElement(typeName, id, icon, location);
        m_nodeToIndex.insert(objDef, index);
        return true;
    }

    void endVisit(AST::UiObjectDefinition *objDef)
    {
        if (!validElement(objDef)) {
            return;
        }
        m_model->leaveElement();
    }

    bool visit(AST::UiScriptBinding *scriptBinding)
    {
        AST::SourceLocation location = getLocation(scriptBinding);

        QModelIndex index = m_model->enterProperty(asString(scriptBinding->qualifiedId), false, location);
        m_nodeToIndex.insert(scriptBinding, index);

        return true;
    }

    void endVisit(AST::UiScriptBinding * /*scriptBinding*/)
    {
        m_model->leaveProperty();
    }

    bool visit(AST::UiPublicMember *publicMember)
    {
        AST::SourceLocation location = getLocation(publicMember);
        QModelIndex index = m_model->enterProperty(publicMember->name->asString(), true, location);
        m_nodeToIndex.insert(publicMember, index);

        return true;
    }

    void endVisit(AST::UiPublicMember * /*publicMember*/)
    {
        m_model->leaveProperty();
    }

    bool validElement(AST::UiObjectDefinition *objDef) {
        // For 'Rectangle { id }', id is parsed as UiObjectDefinition ... Filter this out.
        return objDef->qualifiedTypeNameId->name->asString().at(0).isUpper();
    }

    QIcon getIcon(AST::UiObjectDefinition *objDef) {
        const QmlJS::Interpreter::Value *value = m_context->evaluate(objDef->qualifiedTypeNameId);

        if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) {
            do {
                QString module;
                QString typeName;
                if (const Interpreter::QmlObjectValue *qmlObjectValue =
                        dynamic_cast<const Interpreter::QmlObjectValue*>(objectValue)) {
                    module = qmlObjectValue->packageName();
                }
                typeName = objectValue->className();

                QIcon icon = m_model->m_icons->icon(module, typeName);
                if (! icon.isNull())
                    return icon;

                objectValue = objectValue->prototype(m_context->context());
            } while (objectValue);
        }
        return QIcon();
    }

    QString getId(AST::UiObjectDefinition *objDef) {
        QString id;
        for (AST::UiObjectMemberList *it = objDef->initializer->members; it; it = it->next) {
            if (AST::UiScriptBinding *binding = dynamic_cast<AST::UiScriptBinding*>(it->member)) {
                if (binding->qualifiedId->name->asString() == "id") {
                    AST::ExpressionStatement *expr = dynamic_cast<AST::ExpressionStatement*>(binding->statement);
                    if (!expr)
                        continue;
                    AST::IdentifierExpression *idExpr = dynamic_cast<AST::IdentifierExpression*>(expr->expression);
                    if (!idExpr)
                        continue;
                    id = idExpr->name->asString();
                    break;
                }
            }
        }
        return id;
    }

    AST::SourceLocation getLocation(AST::UiObjectMember *objMember) {
        AST::SourceLocation location;
        location.offset = objMember->firstSourceLocation().offset;
        location.length = objMember->lastSourceLocation().offset
                - objMember->firstSourceLocation().offset
                + objMember->lastSourceLocation().length;
        return location;
    }

    QmlOutlineModel *m_model;
    LookupContext::Ptr m_context;

    QHash<AST::Node*, QModelIndex> m_nodeToIndex;
    QHash<QString, QIcon> m_typeToIcon;
    int indent;
};

QmlOutlineModel::QmlOutlineModel(QObject *parent) :
    QStandardItemModel(parent)
{
    m_icons = Icons::instance();
    const QString resourcePath = Core::ICore::instance()->resourcePath();
    QmlJS::Icons::instance()->setIconFilesPath(resourcePath + "/qmlicons");
}

QmlJS::Document::Ptr QmlOutlineModel::document() const
{
    return m_document;
}

void QmlOutlineModel::update(QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot)
{
    m_document = doc;

    m_treePos.clear();
    m_treePos.append(0);
    m_currentItem = invisibleRootItem();

    QmlOutlineModelSync syncModel(this);
    syncModel(doc, snapshot);

    emit updated();
}

QModelIndex QmlOutlineModel::enterElement(const QString &type, const QString &id, const QIcon &icon, const AST::SourceLocation &sourceLocation)
{
    QStandardItem *item = enterNode(sourceLocation);
    if (!id.isEmpty()) {
        item->setText(id);
    } else {
        item->setText(type);
    }
    item->setIcon(icon);
    item->setToolTip(type);
    return item->index();
}

void QmlOutlineModel::leaveElement()
{
    leaveNode();
}

QModelIndex QmlOutlineModel::enterProperty(const QString &name, bool isCustomProperty, const AST::SourceLocation &sourceLocation)
{
    QStandardItem *item = enterNode(sourceLocation);
    item->setText(name);
    if (isCustomProperty) {
        item->setIcon(m_icons->publicMemberIcon());
    } else {
        item->setIcon(m_icons->scriptBindingIcon());
    }
    return item->index();
}

void QmlOutlineModel::leaveProperty()
{
    leaveNode();
}

QStandardItem *QmlOutlineModel::enterNode(const QmlJS::AST::SourceLocation &location)
{
    int siblingIndex = m_treePos.last();
    if (siblingIndex == 0) {
        // first child
        if (!m_currentItem->hasChildren()) {
            QStandardItem *parentItem = m_currentItem;
            m_currentItem = new QStandardItem;
            m_currentItem->setEditable(false);
            parentItem->appendRow(m_currentItem);
            if (debug)
                qDebug() << "QmlOutlineModel - Adding" << "element to" << parentItem->text();
        } else {
            m_currentItem = m_currentItem->child(0);
        }
    } else {
        // sibling
        if (m_currentItem->rowCount() <= siblingIndex) {
            // attach
            QStandardItem *oldItem = m_currentItem;
            m_currentItem = new QStandardItem;
            m_currentItem->setEditable(false);
            oldItem->appendRow(m_currentItem);
            if (debug)
                qDebug() << "QmlOutlineModel - Adding" << "element to" << oldItem->text();
        } else {
            m_currentItem = m_currentItem->child(siblingIndex);
        }
    }

    m_treePos.append(0);
    m_currentItem->setData(QVariant::fromValue(location), SourceLocationRole);

    return m_currentItem;
}

void QmlOutlineModel::leaveNode()
{
    int lastIndex = m_treePos.takeLast();

    if (lastIndex > 0) {
        // element has children
        if (lastIndex < m_currentItem->rowCount()) {
            if (debug)
                qDebug() << "QmlOutlineModel - removeRows from " << m_currentItem->text() << lastIndex << m_currentItem->rowCount() - lastIndex;
            m_currentItem->removeRows(lastIndex, m_currentItem->rowCount() - lastIndex);
        }
        m_currentItem = parentItem();
    } else {
        if (m_currentItem->hasChildren()) {
            if (debug)
                qDebug() << "QmlOutlineModel - removeRows from " << m_currentItem->text() << 0 << m_currentItem->rowCount();
            m_currentItem->removeRows(0, m_currentItem->rowCount());
        }
        m_currentItem = parentItem();
    }

    m_treePos.last()++;
}

QStandardItem *QmlOutlineModel::parentItem()
{
    QStandardItem *parent = m_currentItem->parent();
    if (!parent)
        parent = invisibleRootItem();
    return parent;
}

} // namespace Internal
} // namespace QmlJSEditor
<commit_msg>QmlOutline: Disable debugging output<commit_after>#include "qmloutlinemodel.h"
#include <qmljs/parser/qmljsastvisitor_p.h>
#include <qmljs/qmljsinterpreter.h>
#include <qmljs/qmljslookupcontext.h>

#include <coreplugin/icore.h>
#include <QtCore/QDebug>
#include <QtCore/QTime>
#include <typeinfo>

using namespace QmlJS;
using namespace QmlJSEditor::Internal;

enum {
    debug = false
};

namespace QmlJSEditor {
namespace Internal {

class QmlOutlineModelSync : protected AST::Visitor
{
public:
    QmlOutlineModelSync(QmlOutlineModel *model) :
        m_model(model),
        indent(0)
    {
    }

    void operator()(Document::Ptr doc, const Snapshot &snapshot)
    {
        m_nodeToIndex.clear();

        // Set up lookup context once to do the element type lookup
        //
        // We're simplifying here by using the root context everywhere
        // (empty node list). However, creating the LookupContext is quite expensive (about 3ms),
        // and there is AFAIK no way to introduce new type names in a sub-context.
        m_context = LookupContext::create(doc, snapshot, QList<AST::Node*>());

        if (debug)
            qDebug() << "QmlOutlineModel ------";
        if (doc && doc->ast())
            doc->ast()->accept(this);

        m_context.clear();
    }

private:
    bool preVisit(AST::Node *node)
    {
        if (!node)
            return false;
        if (debug)
            qDebug() << "QmlOutlineModel -" << QByteArray(indent++, '-').constData() << node << typeid(*node).name();
        return true;
    }

    void postVisit(AST::Node *)
    {
        indent--;
    }

    QString asString(AST::UiQualifiedId *id)
    {
        QString text;
        for (; id; id = id->next) {
            if (id->name)
                text += id->name->asString();
            else
                text += QLatin1Char('?');

            if (id->next)
                text += QLatin1Char('.');
        }

        return text;
    }

    typedef QPair<QString,QString> ElementType;
    bool visit(AST::UiObjectDefinition *objDef)
    {
        if (!validElement(objDef)) {
            return true;
        }

        AST::SourceLocation location = getLocation(objDef);

        const QString typeName = asString(objDef->qualifiedTypeNameId);

        if (!m_typeToIcon.contains(typeName)) {
            m_typeToIcon.insert(typeName, getIcon(objDef));
        }
        const QIcon icon = m_typeToIcon.value(typeName);
        QString id = getId(objDef);

        QModelIndex index = m_model->enterElement(typeName, id, icon, location);
        m_nodeToIndex.insert(objDef, index);
        return true;
    }

    void endVisit(AST::UiObjectDefinition *objDef)
    {
        if (!validElement(objDef)) {
            return;
        }
        m_model->leaveElement();
    }

    bool visit(AST::UiScriptBinding *scriptBinding)
    {
        AST::SourceLocation location = getLocation(scriptBinding);

        QModelIndex index = m_model->enterProperty(asString(scriptBinding->qualifiedId), false, location);
        m_nodeToIndex.insert(scriptBinding, index);

        return true;
    }

    void endVisit(AST::UiScriptBinding * /*scriptBinding*/)
    {
        m_model->leaveProperty();
    }

    bool visit(AST::UiPublicMember *publicMember)
    {
        AST::SourceLocation location = getLocation(publicMember);
        QModelIndex index = m_model->enterProperty(publicMember->name->asString(), true, location);
        m_nodeToIndex.insert(publicMember, index);

        return true;
    }

    void endVisit(AST::UiPublicMember * /*publicMember*/)
    {
        m_model->leaveProperty();
    }

    bool validElement(AST::UiObjectDefinition *objDef) {
        // For 'Rectangle { id }', id is parsed as UiObjectDefinition ... Filter this out.
        return objDef->qualifiedTypeNameId->name->asString().at(0).isUpper();
    }

    QIcon getIcon(AST::UiObjectDefinition *objDef) {
        const QmlJS::Interpreter::Value *value = m_context->evaluate(objDef->qualifiedTypeNameId);

        if (const Interpreter::ObjectValue *objectValue = value->asObjectValue()) {
            do {
                QString module;
                QString typeName;
                if (const Interpreter::QmlObjectValue *qmlObjectValue =
                        dynamic_cast<const Interpreter::QmlObjectValue*>(objectValue)) {
                    module = qmlObjectValue->packageName();
                }
                typeName = objectValue->className();

                QIcon icon = m_model->m_icons->icon(module, typeName);
                if (! icon.isNull())
                    return icon;

                objectValue = objectValue->prototype(m_context->context());
            } while (objectValue);
        }
        return QIcon();
    }

    QString getId(AST::UiObjectDefinition *objDef) {
        QString id;
        for (AST::UiObjectMemberList *it = objDef->initializer->members; it; it = it->next) {
            if (AST::UiScriptBinding *binding = dynamic_cast<AST::UiScriptBinding*>(it->member)) {
                if (binding->qualifiedId->name->asString() == "id") {
                    AST::ExpressionStatement *expr = dynamic_cast<AST::ExpressionStatement*>(binding->statement);
                    if (!expr)
                        continue;
                    AST::IdentifierExpression *idExpr = dynamic_cast<AST::IdentifierExpression*>(expr->expression);
                    if (!idExpr)
                        continue;
                    id = idExpr->name->asString();
                    break;
                }
            }
        }
        return id;
    }

    AST::SourceLocation getLocation(AST::UiObjectMember *objMember) {
        AST::SourceLocation location;
        location.offset = objMember->firstSourceLocation().offset;
        location.length = objMember->lastSourceLocation().offset
                - objMember->firstSourceLocation().offset
                + objMember->lastSourceLocation().length;
        return location;
    }

    QmlOutlineModel *m_model;
    LookupContext::Ptr m_context;

    QHash<AST::Node*, QModelIndex> m_nodeToIndex;
    QHash<QString, QIcon> m_typeToIcon;
    int indent;
};

QmlOutlineModel::QmlOutlineModel(QObject *parent) :
    QStandardItemModel(parent)
{
    m_icons = Icons::instance();
    const QString resourcePath = Core::ICore::instance()->resourcePath();
    QmlJS::Icons::instance()->setIconFilesPath(resourcePath + "/qmlicons");
}

QmlJS::Document::Ptr QmlOutlineModel::document() const
{
    return m_document;
}

void QmlOutlineModel::update(QmlJS::Document::Ptr doc, const QmlJS::Snapshot &snapshot)
{
    m_document = doc;

    m_treePos.clear();
    m_treePos.append(0);
    m_currentItem = invisibleRootItem();

    QmlOutlineModelSync syncModel(this);
    syncModel(doc, snapshot);

    emit updated();
}

QModelIndex QmlOutlineModel::enterElement(const QString &type, const QString &id, const QIcon &icon, const AST::SourceLocation &sourceLocation)
{
    QStandardItem *item = enterNode(sourceLocation);
    if (!id.isEmpty()) {
        item->setText(id);
    } else {
        item->setText(type);
    }
    item->setIcon(icon);
    item->setToolTip(type);
    return item->index();
}

void QmlOutlineModel::leaveElement()
{
    leaveNode();
}

QModelIndex QmlOutlineModel::enterProperty(const QString &name, bool isCustomProperty, const AST::SourceLocation &sourceLocation)
{
    QStandardItem *item = enterNode(sourceLocation);
    item->setText(name);
    if (isCustomProperty) {
        item->setIcon(m_icons->publicMemberIcon());
    } else {
        item->setIcon(m_icons->scriptBindingIcon());
    }
    return item->index();
}

void QmlOutlineModel::leaveProperty()
{
    leaveNode();
}

QStandardItem *QmlOutlineModel::enterNode(const QmlJS::AST::SourceLocation &location)
{
    int siblingIndex = m_treePos.last();
    if (siblingIndex == 0) {
        // first child
        if (!m_currentItem->hasChildren()) {
            QStandardItem *parentItem = m_currentItem;
            m_currentItem = new QStandardItem;
            m_currentItem->setEditable(false);
            parentItem->appendRow(m_currentItem);
            if (debug)
                qDebug() << "QmlOutlineModel - Adding" << "element to" << parentItem->text();
        } else {
            m_currentItem = m_currentItem->child(0);
        }
    } else {
        // sibling
        if (m_currentItem->rowCount() <= siblingIndex) {
            // attach
            QStandardItem *oldItem = m_currentItem;
            m_currentItem = new QStandardItem;
            m_currentItem->setEditable(false);
            oldItem->appendRow(m_currentItem);
            if (debug)
                qDebug() << "QmlOutlineModel - Adding" << "element to" << oldItem->text();
        } else {
            m_currentItem = m_currentItem->child(siblingIndex);
        }
    }

    m_treePos.append(0);
    m_currentItem->setData(QVariant::fromValue(location), SourceLocationRole);

    return m_currentItem;
}

void QmlOutlineModel::leaveNode()
{
    int lastIndex = m_treePos.takeLast();

    if (lastIndex > 0) {
        // element has children
        if (lastIndex < m_currentItem->rowCount()) {
            if (debug)
                qDebug() << "QmlOutlineModel - removeRows from " << m_currentItem->text() << lastIndex << m_currentItem->rowCount() - lastIndex;
            m_currentItem->removeRows(lastIndex, m_currentItem->rowCount() - lastIndex);
        }
        m_currentItem = parentItem();
    } else {
        if (m_currentItem->hasChildren()) {
            if (debug)
                qDebug() << "QmlOutlineModel - removeRows from " << m_currentItem->text() << 0 << m_currentItem->rowCount();
            m_currentItem->removeRows(0, m_currentItem->rowCount());
        }
        m_currentItem = parentItem();
    }

    m_treePos.last()++;
}

QStandardItem *QmlOutlineModel::parentItem()
{
    QStandardItem *parent = m_currentItem->parent();
    if (!parent)
        parent = invisibleRootItem();
    return parent;
}

} // namespace Internal
} // namespace QmlJSEditor
<|endoftext|>


<commit_before>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "completionwidget.h"
#include "completionsupport.h"
#include "icompletioncollector.h"

#include <texteditor/itexteditable.h>
#include <utils/qtcassert.h>

#include <QtCore/QEvent>
#include <QtGui/QApplication>
#include <QtGui/QDesktopWidget>
#include <QtGui/QKeyEvent>
#include <QtGui/QVBoxLayout>
#include <QtGui/QScrollBar>
#include <QtGui/QLabel>
#include <QtGui/QStylePainter>

#include <limits.h>

using namespace TextEditor;
using namespace TextEditor::Internal;

#define NUMBER_OF_VISIBLE_ITEMS 10

namespace TextEditor {
namespace Internal {

class AutoCompletionModel : public QAbstractListModel
{
public:
    AutoCompletionModel(QObject *parent);

    inline const CompletionItem &itemAt(const QModelIndex &index) const
    { return m_items.at(index.row()); }

    void setItems(const QList<CompletionItem> &items);

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

private:
    QList<CompletionItem> m_items;
};

class CompletionInfoFrame : public QLabel {
public:

    CompletionInfoFrame(QWidget *parent = 0) :
            QLabel(parent, Qt::ToolTip | Qt::WindowStaysOnTopHint)
    {
        setFocusPolicy(Qt::NoFocus);
        setAttribute(Qt::WA_DeleteOnClose);
        setAutoFillBackground(true);
        setBackgroundRole(QPalette::ToolTipBase);
        setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, this));
        setIndent(1);
    }

    void paintEvent(QPaintEvent *e) {
        QStylePainter p(this);
        QStyleOptionFrame opt;
        opt.init(this);
        p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
        p.end();
        QLabel::paintEvent(e);
    }
    void resizeEvent(QResizeEvent *e)
    {
        QStyleHintReturnMask frameMask;
        QStyleOption option;
        option.init(this);
        if (style()->styleHint(QStyle::SH_ToolTip_Mask, &option, this, &frameMask))
            setMask(frameMask.region);
        QLabel::resizeEvent(e);
    }
};

} // namespace Internal
} // namespace TextEditor

AutoCompletionModel::AutoCompletionModel(QObject *parent)
    : QAbstractListModel(parent)
{
}

void AutoCompletionModel::setItems(const QList<CompletionItem> &items)
{
    m_items = items;
    reset();
}

int AutoCompletionModel::rowCount(const QModelIndex &) const
{
    return m_items.count();
}

QVariant AutoCompletionModel::data(const QModelIndex &index, int role) const
{
    if (index.row() >= m_items.count())
        return QVariant();

    if (role == Qt::DisplayRole) {
        return itemAt(index).text;
    } else if (role == Qt::DecorationRole) {
        return itemAt(index).icon;
    } else if (role == Qt::WhatsThisRole) {
        return itemAt(index).details;
    }

    return QVariant();
}

CompletionWidget::CompletionWidget(CompletionSupport *support, ITextEditable *editor)
    : QFrame(0, Qt::Popup),
      m_support(support),
      m_editor(editor),
      m_completionListView(new CompletionListView(support, editor, this))
{
    // We disable the frame on this list view and use a QFrame around it instead.
    // This improves the look with QGTKStyle.
#ifndef Q_WS_MAC
    setFrameStyle(m_completionListView->frameStyle());
#endif
    m_completionListView->setFrameStyle(QFrame::NoFrame);

    setObjectName(QLatin1String("m_popupFrame"));
    setAttribute(Qt::WA_DeleteOnClose);
    setMinimumSize(1, 1);

    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->setMargin(0);

    layout->addWidget(m_completionListView);
    setFocusProxy(m_completionListView);

    connect(m_completionListView, SIGNAL(itemSelected(TextEditor::CompletionItem)),
            this, SIGNAL(itemSelected(TextEditor::CompletionItem)));
    connect(m_completionListView, SIGNAL(completionListClosed()),
            this, SIGNAL(completionListClosed()));
    connect(m_completionListView, SIGNAL(activated(QModelIndex)),
            SLOT(closeList(QModelIndex)));
    connect(editor, SIGNAL(contentsChangedBecauseOfUndo()),
            this, SLOT(closeList()));
}

CompletionWidget::~CompletionWidget()
{
}

void CompletionWidget::setQuickFix(bool quickFix)
{
    m_completionListView->setQuickFix(quickFix);
}

void CompletionWidget::setCompletionItems(const QList<TextEditor::CompletionItem> &completionitems)
{
    m_completionListView->setCompletionItems(completionitems);
}

void CompletionWidget::closeList(const QModelIndex &index)
{
    m_completionListView->closeList(index);
    close();
}

void CompletionWidget::showCompletions(int startPos)
{
    ensurePolished();
    updatePositionAndSize(startPos);
    show();
    setFocus();
}

void CompletionWidget::updatePositionAndSize(int startPos)
{
    // Determine size by calculating the space of the visible items
    QAbstractItemModel *model = m_completionListView->model();
    int visibleItems = model->rowCount();
    if (visibleItems > NUMBER_OF_VISIBLE_ITEMS)
        visibleItems = NUMBER_OF_VISIBLE_ITEMS;

    const QStyleOptionViewItem &option = m_completionListView->viewOptions();

    QSize shint;
    for (int i = 0; i < visibleItems; ++i) {
        QSize tmp = m_completionListView->itemDelegate()->sizeHint(option, model->index(i, 0));
        if (shint.width() < tmp.width())
            shint = tmp;
    }

    const int fw = frameWidth();
    const int width = shint.width() + fw * 2 + 30;
    const int height = shint.height() * visibleItems + fw * 2;

    // Determine the position, keeping the popup on the screen
    const QRect cursorRect = m_editor->cursorRect(startPos);
    const QDesktopWidget *desktop = QApplication::desktop();

    QWidget *editorWidget = m_editor->widget();

#ifdef Q_WS_MAC
    const QRect screen = desktop->availableGeometry(desktop->screenNumber(editorWidget));
#else
    const QRect screen = desktop->screenGeometry(desktop->screenNumber(editorWidget));
#endif

    QPoint pos = cursorRect.bottomLeft();
    pos.rx() -= 16 + fw;    // Space for the icons

    if (pos.y() + height > screen.bottom())
        pos.setY(cursorRect.top() - height);

    if (pos.x() + width > screen.right())
        pos.setX(screen.right() - width);

    setGeometry(pos.x(), pos.y(), width, height);
}

CompletionListView::CompletionListView(CompletionSupport *support, ITextEditable *editor, CompletionWidget *completionWidget)
    : QListView(completionWidget),
      m_blockFocusOut(false),
      m_quickFix(false),
      m_editor(editor),
      m_editorWidget(editor->widget()),
      m_completionWidget(completionWidget),
      m_model(new AutoCompletionModel(this)),
      m_support(support)
{
    QTC_ASSERT(m_editorWidget, return);

    setAttribute(Qt::WA_MacShowFocusRect, false);
    setUniformItemSizes(true);
    setSelectionBehavior(QAbstractItemView::SelectItems);
    setSelectionMode(QAbstractItemView::SingleSelection);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setMinimumSize(1, 1);
    setModel(m_model);
#ifdef Q_WS_MAC
    if (horizontalScrollBar())
        horizontalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
    if (verticalScrollBar())
        verticalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
#endif

}

CompletionListView::~CompletionListView()
{
}

void CompletionListView::maybeShowInfoTip()
{
    QModelIndex current = currentIndex();
    if (!current.isValid())
        return;
    QString infoTip = current.data(Qt::WhatsThisRole).toString();

    if (infoTip.isEmpty()) {
        delete m_infoFrame.data();
        return;
    }

    if (m_infoFrame.isNull())
        m_infoFrame = new CompletionInfoFrame(this);

    QRect r = rectForIndex(current);
    m_infoFrame->move(
            (parentWidget()->mapToGlobal(
                    parentWidget()->rect().topRight() + QPoint(2, 0))).x(),
            mapToGlobal(r.topRight()).y() - verticalOffset()
            );
    m_infoFrame->setText(infoTip);
    m_infoFrame->adjustSize();
    m_infoFrame->show();
}

void CompletionListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    QListView::currentChanged(current, previous);
    if (isVisible())
        maybeShowInfoTip();
}

bool CompletionListView::event(QEvent *e)
{
    if (e->type() == QEvent::Show) {
        bool b = QListView::event(e);
        maybeShowInfoTip();
        return b;
    }

    if (m_blockFocusOut)
        return QListView::event(e);

    bool forwardKeys = true;
    if (e->type() == QEvent::FocusOut) {
        QModelIndex index;
#if defined(Q_OS_DARWIN) && ! defined(QT_MAC_USE_COCOA)
        QFocusEvent *fe = static_cast<QFocusEvent *>(e);
        if (fe->reason() == Qt::OtherFocusReason) {
            // Qt/carbon workaround
            // focus out is received before the key press event.
            index = currentIndex();
        }
#endif
        m_completionWidget->closeList(index);
        return true;
    } else if (e->type() == QEvent::ShortcutOverride) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(e);
        switch (ke->key()) {
        case Qt::Key_N:
        case Qt::Key_P:
            // select next/previous completion
            if (ke->modifiers() == Qt::ControlModifier)
            {
                e->accept();
                int change = (ke->key() == Qt::Key_N) ? 1 : -1;
                int nrows = model()->rowCount();
                int row = currentIndex().row();
                int newRow = (row + change + nrows) % nrows;
                if (newRow == row + change || !ke->isAutoRepeat())
                    setCurrentIndex(m_model->index(newRow));
                return true;
            }
        }
    } else if (e->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(e);
        switch (ke->key()) {
        case Qt::Key_N:
        case Qt::Key_P:
            // select next/previous completion - so don't pass on to editor
            if (ke->modifiers() == Qt::ControlModifier)
                forwardKeys = false;
            break;

        case Qt::Key_Escape:
            m_completionWidget->closeList();
            return true;

        case Qt::Key_Right:
        case Qt::Key_Left:
            break;

        case Qt::Key_Tab:
        case Qt::Key_Return:
            //independently from style, accept current entry if return is pressed
            if (qApp->focusWidget() == this)
                m_completionWidget->closeList(currentIndex());
            return true;

        case Qt::Key_Up:
            if (!ke->isAutoRepeat()
                && currentIndex().row() == 0) {
                setCurrentIndex(model()->index(model()->rowCount()-1, 0));
                return true;
            }
            forwardKeys = false;
            break;

        case Qt::Key_Down:
            if (!ke->isAutoRepeat()
                && currentIndex().row() == model()->rowCount()-1) {
                setCurrentIndex(model()->index(0, 0));
                return true;
            }
            forwardKeys = false;
            break;

        case Qt::Key_Enter:
        case Qt::Key_PageDown:
        case Qt::Key_PageUp:
            forwardKeys = false;
            break;

        default:
            // if a key is forwarded, completion widget is re-opened and selected item is reset to first,
            // so only forward keys that insert text and refine the completed item
            forwardKeys = !ke->text().isEmpty();
            break;
        }

        if (forwardKeys && ! m_quickFix) {
            m_blockFocusOut = true;
            QApplication::sendEvent(m_editorWidget, e);
            m_blockFocusOut = false;

            // Have the completion support update the list of items
            m_support->autoComplete(m_editor, false);

            return true;
        }
    }
    return QListView::event(e);
}

void CompletionListView::keyboardSearch(const QString &search)
{
    Q_UNUSED(search)
}

void CompletionListView::setQuickFix(bool quickFix)
{
    m_quickFix = quickFix;
}

void CompletionListView::setCompletionItems(const QList<TextEditor::CompletionItem> &completionItems)
{
    m_model->setItems(completionItems);

    // Select the first of the most relevant completion items
    int relevance = INT_MIN;
    int mostRelevantIndex = 0;
    for (int i = 0; i < completionItems.size(); ++i) {
        const CompletionItem &item = completionItems.at(i);
        if (item.relevance > relevance) {
            relevance = item.relevance;
            mostRelevantIndex = i;
        }
    }

    setCurrentIndex(m_model->index(mostRelevantIndex));
}

void CompletionListView::closeList(const QModelIndex &index)
{
    m_blockFocusOut = true;

    if (index.isValid())
        emit itemSelected(m_model->itemAt(index));

    emit completionListClosed();

    m_blockFocusOut = false;
}
<commit_msg>use correct tooltip foreground role and drop the unnecessary auto fill<commit_after>/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://qt.nokia.com/contact.
**
**************************************************************************/

#include "completionwidget.h"
#include "completionsupport.h"
#include "icompletioncollector.h"

#include <texteditor/itexteditable.h>
#include <utils/qtcassert.h>

#include <QtCore/QEvent>
#include <QtGui/QApplication>
#include <QtGui/QDesktopWidget>
#include <QtGui/QKeyEvent>
#include <QtGui/QVBoxLayout>
#include <QtGui/QScrollBar>
#include <QtGui/QLabel>
#include <QtGui/QStylePainter>
#include <QtGui/QToolTip>

#include <limits.h>

using namespace TextEditor;
using namespace TextEditor::Internal;

#define NUMBER_OF_VISIBLE_ITEMS 10

namespace TextEditor {
namespace Internal {

class AutoCompletionModel : public QAbstractListModel
{
public:
    AutoCompletionModel(QObject *parent);

    inline const CompletionItem &itemAt(const QModelIndex &index) const
    { return m_items.at(index.row()); }

    void setItems(const QList<CompletionItem> &items);

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

private:
    QList<CompletionItem> m_items;
};

class CompletionInfoFrame : public QLabel {
public:

    CompletionInfoFrame(QWidget *parent = 0) :
            QLabel(parent, Qt::ToolTip | Qt::WindowStaysOnTopHint)
    {
        setFocusPolicy(Qt::NoFocus);
        setAttribute(Qt::WA_DeleteOnClose);
        setForegroundRole(QPalette::ToolTipText);
        setBackgroundRole(QPalette::ToolTipBase);
        setMargin(1 + style()->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, this));

        setIndent(1);
    }

    void paintEvent(QPaintEvent *e) {
        QStylePainter p(this);
        QStyleOptionFrame opt;
        opt.init(this);
        p.drawPrimitive(QStyle::PE_PanelTipLabel, opt);
        p.end();
        QLabel::paintEvent(e);
    }
    void resizeEvent(QResizeEvent *e)
    {
        QStyleHintReturnMask frameMask;
        QStyleOption option;
        option.init(this);
        if (style()->styleHint(QStyle::SH_ToolTip_Mask, &option, this, &frameMask))
            setMask(frameMask.region);
        QLabel::resizeEvent(e);
    }
};

} // namespace Internal
} // namespace TextEditor

AutoCompletionModel::AutoCompletionModel(QObject *parent)
    : QAbstractListModel(parent)
{
}

void AutoCompletionModel::setItems(const QList<CompletionItem> &items)
{
    m_items = items;
    reset();
}

int AutoCompletionModel::rowCount(const QModelIndex &) const
{
    return m_items.count();
}

QVariant AutoCompletionModel::data(const QModelIndex &index, int role) const
{
    if (index.row() >= m_items.count())
        return QVariant();

    if (role == Qt::DisplayRole) {
        return itemAt(index).text;
    } else if (role == Qt::DecorationRole) {
        return itemAt(index).icon;
    } else if (role == Qt::WhatsThisRole) {
        return itemAt(index).details;
    }

    return QVariant();
}

CompletionWidget::CompletionWidget(CompletionSupport *support, ITextEditable *editor)
    : QFrame(0, Qt::Popup),
      m_support(support),
      m_editor(editor),
      m_completionListView(new CompletionListView(support, editor, this))
{
    // We disable the frame on this list view and use a QFrame around it instead.
    // This improves the look with QGTKStyle.
#ifndef Q_WS_MAC
    setFrameStyle(m_completionListView->frameStyle());
#endif
    m_completionListView->setFrameStyle(QFrame::NoFrame);

    setObjectName(QLatin1String("m_popupFrame"));
    setAttribute(Qt::WA_DeleteOnClose);
    setMinimumSize(1, 1);

    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->setMargin(0);

    layout->addWidget(m_completionListView);
    setFocusProxy(m_completionListView);

    connect(m_completionListView, SIGNAL(itemSelected(TextEditor::CompletionItem)),
            this, SIGNAL(itemSelected(TextEditor::CompletionItem)));
    connect(m_completionListView, SIGNAL(completionListClosed()),
            this, SIGNAL(completionListClosed()));
    connect(m_completionListView, SIGNAL(activated(QModelIndex)),
            SLOT(closeList(QModelIndex)));
    connect(editor, SIGNAL(contentsChangedBecauseOfUndo()),
            this, SLOT(closeList()));
}

CompletionWidget::~CompletionWidget()
{
}

void CompletionWidget::setQuickFix(bool quickFix)
{
    m_completionListView->setQuickFix(quickFix);
}

void CompletionWidget::setCompletionItems(const QList<TextEditor::CompletionItem> &completionitems)
{
    m_completionListView->setCompletionItems(completionitems);
}

void CompletionWidget::closeList(const QModelIndex &index)
{
    m_completionListView->closeList(index);
    close();
}

void CompletionWidget::showCompletions(int startPos)
{
    ensurePolished();
    updatePositionAndSize(startPos);
    show();
    setFocus();
}

void CompletionWidget::updatePositionAndSize(int startPos)
{
    // Determine size by calculating the space of the visible items
    QAbstractItemModel *model = m_completionListView->model();
    int visibleItems = model->rowCount();
    if (visibleItems > NUMBER_OF_VISIBLE_ITEMS)
        visibleItems = NUMBER_OF_VISIBLE_ITEMS;

    const QStyleOptionViewItem &option = m_completionListView->viewOptions();

    QSize shint;
    for (int i = 0; i < visibleItems; ++i) {
        QSize tmp = m_completionListView->itemDelegate()->sizeHint(option, model->index(i, 0));
        if (shint.width() < tmp.width())
            shint = tmp;
    }

    const int fw = frameWidth();
    const int width = shint.width() + fw * 2 + 30;
    const int height = shint.height() * visibleItems + fw * 2;

    // Determine the position, keeping the popup on the screen
    const QRect cursorRect = m_editor->cursorRect(startPos);
    const QDesktopWidget *desktop = QApplication::desktop();

    QWidget *editorWidget = m_editor->widget();

#ifdef Q_WS_MAC
    const QRect screen = desktop->availableGeometry(desktop->screenNumber(editorWidget));
#else
    const QRect screen = desktop->screenGeometry(desktop->screenNumber(editorWidget));
#endif

    QPoint pos = cursorRect.bottomLeft();
    pos.rx() -= 16 + fw;    // Space for the icons

    if (pos.y() + height > screen.bottom())
        pos.setY(cursorRect.top() - height);

    if (pos.x() + width > screen.right())
        pos.setX(screen.right() - width);

    setGeometry(pos.x(), pos.y(), width, height);
}

CompletionListView::CompletionListView(CompletionSupport *support, ITextEditable *editor, CompletionWidget *completionWidget)
    : QListView(completionWidget),
      m_blockFocusOut(false),
      m_quickFix(false),
      m_editor(editor),
      m_editorWidget(editor->widget()),
      m_completionWidget(completionWidget),
      m_model(new AutoCompletionModel(this)),
      m_support(support)
{
    QTC_ASSERT(m_editorWidget, return);

    setAttribute(Qt::WA_MacShowFocusRect, false);
    setUniformItemSizes(true);
    setSelectionBehavior(QAbstractItemView::SelectItems);
    setSelectionMode(QAbstractItemView::SingleSelection);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setMinimumSize(1, 1);
    setModel(m_model);
#ifdef Q_WS_MAC
    if (horizontalScrollBar())
        horizontalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
    if (verticalScrollBar())
        verticalScrollBar()->setAttribute(Qt::WA_MacMiniSize);
#endif

}

CompletionListView::~CompletionListView()
{
}

void CompletionListView::maybeShowInfoTip()
{
    QModelIndex current = currentIndex();
    if (!current.isValid())
        return;
    QString infoTip = current.data(Qt::WhatsThisRole).toString();

    if (infoTip.isEmpty()) {
        delete m_infoFrame.data();
        return;
    }

    if (m_infoFrame.isNull())
        m_infoFrame = new CompletionInfoFrame(this);

    QRect r = rectForIndex(current);
    m_infoFrame->move(
            (parentWidget()->mapToGlobal(
                    parentWidget()->rect().topRight() + QPoint(2, 0))).x(),
            mapToGlobal(r.topRight()).y() - verticalOffset()
            );
    m_infoFrame->setText(infoTip);
    m_infoFrame->adjustSize();
    m_infoFrame->show();
}

void CompletionListView::currentChanged(const QModelIndex &current, const QModelIndex &previous)
{
    QListView::currentChanged(current, previous);
    if (isVisible())
        maybeShowInfoTip();
}

bool CompletionListView::event(QEvent *e)
{
    if (e->type() == QEvent::Show) {
        bool b = QListView::event(e);
        maybeShowInfoTip();
        return b;
    }

    if (m_blockFocusOut)
        return QListView::event(e);

    bool forwardKeys = true;
    if (e->type() == QEvent::FocusOut) {
        QModelIndex index;
#if defined(Q_OS_DARWIN) && ! defined(QT_MAC_USE_COCOA)
        QFocusEvent *fe = static_cast<QFocusEvent *>(e);
        if (fe->reason() == Qt::OtherFocusReason) {
            // Qt/carbon workaround
            // focus out is received before the key press event.
            index = currentIndex();
        }
#endif
        m_completionWidget->closeList(index);
        return true;
    } else if (e->type() == QEvent::ShortcutOverride) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(e);
        switch (ke->key()) {
        case Qt::Key_N:
        case Qt::Key_P:
            // select next/previous completion
            if (ke->modifiers() == Qt::ControlModifier)
            {
                e->accept();
                int change = (ke->key() == Qt::Key_N) ? 1 : -1;
                int nrows = model()->rowCount();
                int row = currentIndex().row();
                int newRow = (row + change + nrows) % nrows;
                if (newRow == row + change || !ke->isAutoRepeat())
                    setCurrentIndex(m_model->index(newRow));
                return true;
            }
        }
    } else if (e->type() == QEvent::KeyPress) {
        QKeyEvent *ke = static_cast<QKeyEvent *>(e);
        switch (ke->key()) {
        case Qt::Key_N:
        case Qt::Key_P:
            // select next/previous completion - so don't pass on to editor
            if (ke->modifiers() == Qt::ControlModifier)
                forwardKeys = false;
            break;

        case Qt::Key_Escape:
            m_completionWidget->closeList();
            return true;

        case Qt::Key_Right:
        case Qt::Key_Left:
            break;

        case Qt::Key_Tab:
        case Qt::Key_Return:
            //independently from style, accept current entry if return is pressed
            if (qApp->focusWidget() == this)
                m_completionWidget->closeList(currentIndex());
            return true;

        case Qt::Key_Up:
            if (!ke->isAutoRepeat()
                && currentIndex().row() == 0) {
                setCurrentIndex(model()->index(model()->rowCount()-1, 0));
                return true;
            }
            forwardKeys = false;
            break;

        case Qt::Key_Down:
            if (!ke->isAutoRepeat()
                && currentIndex().row() == model()->rowCount()-1) {
                setCurrentIndex(model()->index(0, 0));
                return true;
            }
            forwardKeys = false;
            break;

        case Qt::Key_Enter:
        case Qt::Key_PageDown:
        case Qt::Key_PageUp:
            forwardKeys = false;
            break;

        default:
            // if a key is forwarded, completion widget is re-opened and selected item is reset to first,
            // so only forward keys that insert text and refine the completed item
            forwardKeys = !ke->text().isEmpty();
            break;
        }

        if (forwardKeys && ! m_quickFix) {
            m_blockFocusOut = true;
            QApplication::sendEvent(m_editorWidget, e);
            m_blockFocusOut = false;

            // Have the completion support update the list of items
            m_support->autoComplete(m_editor, false);

            return true;
        }
    }
    return QListView::event(e);
}

void CompletionListView::keyboardSearch(const QString &search)
{
    Q_UNUSED(search)
}

void CompletionListView::setQuickFix(bool quickFix)
{
    m_quickFix = quickFix;
}

void CompletionListView::setCompletionItems(const QList<TextEditor::CompletionItem> &completionItems)
{
    m_model->setItems(completionItems);

    // Select the first of the most relevant completion items
    int relevance = INT_MIN;
    int mostRelevantIndex = 0;
    for (int i = 0; i < completionItems.size(); ++i) {
        const CompletionItem &item = completionItems.at(i);
        if (item.relevance > relevance) {
            relevance = item.relevance;
            mostRelevantIndex = i;
        }
    }

    setCurrentIndex(m_model->index(mostRelevantIndex));
}

void CompletionListView::closeList(const QModelIndex &index)
{
    m_blockFocusOut = true;

    if (index.isValid())
        emit itemSelected(m_model->itemAt(index));

    emit completionListClosed();

    m_blockFocusOut = false;
}
<|endoftext|>